Conversation
[codex] add zero-channel placeholders and remaining workspace changes
…tion and error recovery mechanisms.
…r-tokens feat: add masked token placeholders and desktop icons
…nagement UI - Add localCallbackServer for handling OAuth callback requests. - Implement service functions for managing OAuth sessions and accounts. - Create CodexAdapter for platform-specific interactions. - Develop OAuthManagement page for managing OAuth connections and providers. - Add tests for localCallbackServer and OAuthManagement components.
…agement feat: implement Codex OAuth flow with loopback callback server and ma…
feat: add multi-provider OAuth support for Claude and Gemini CLI
…i conversion When requests arrive via OpenAI-compatible endpoints and are converted to Gemini format, functionCall parts were missing thoughtSignature. Gemini 3+ rejects such requests with 400 when thinking mode is enabled. Changes: - Extract thoughtSignature from tool_calls provider_specific_fields - Inject into corresponding functionCall parts - Fall back to dummy sentinel when thinking is enabled but no signature available - Split signed/unsigned parts into separate model messages (Gemini requirement) All existing tests pass (59/59).
Fixes cita-777#130 The inbound normalizer forwarded all unknown top-level fields to Gemini API. Client-specific fields like requestId caused upstream 400 errors. Changed from blocklist (only 3 entries) to allowlist of known Gemini API fields: contents, systemInstruction, cachedContent, safetySettings, generationConfig, tools, toolConfig, labels, model.
…-migration fix: recover site unique index bootstrap
feat: align oauth loopback flow with cliproxyapi
…gnature-tool-calls fix: inject thoughtSignature into functionCall parts for OpenAI→Gemini conversion
…own-request-fields fix: use allowlist for Gemini native request fields to prevent 400 on unknown fields
fix: preserve codex workspaces and clarify oauth guidance
feat: expand gemini proxy compatibility matrix
fix: improve oauth fallback handling for Gemini and Antigravity
…iproxyapi fix: align oauth onboarding with cliproxyapi
fix: tighten CI typecheck coverage
* fix: preserve protocol parity across claude fallbacks * fix: tighten protocol parity across responses and anthropic fallbacks * fix: preserve stricter tool contracts across fallbacks * fix: unblock anthropic stream server build
… 续传与代理重试稳定性 (cita-777#257) * wip: preserve local changes before upstream merge * Improve proxy routing stability and add stable-first strategy * Sync explicit-group routing strategy to eligible source routes * Fix proxy retry and runtime health review issues * fix: make proxy channel retry attempts configurable * fix: harden proxy retry recovery and bookkeeping * fix: address pr 257 review comments --------- Co-authored-by: Cita <juricek.chen@gmail.com>
* fix: exclude runtime db config from backup and migration * fix: restore docker builder install deps
* wip: preserve local changes before upstream merge * Improve proxy routing stability and add stable-first strategy * Sync explicit-group routing strategy to eligible source routes * Fix proxy retry and runtime health review issues * fix: make proxy channel retry attempts configurable * fix: harden proxy retry recovery and bookkeeping * fix: address pr 257 review comments * fix: preserve oe tool_result structure * fix: preserve protocol parity across compatibility hops * fix: satisfy server build and typecheck * fix: preserve codex and mcp fallback semantics * fix: address ci and review follow-ups --------- Co-authored-by: bnvnvnv <bnvnvnv@gmail.com>
* fix: default downstream key access selections to all * test: fix downstream keys test renderer typing
…ordination / 优化代理通道故障切换与粘滞会话协调 (cita-777#268) * wip: preserve local changes before upstream merge * Improve proxy routing stability and add stable-first strategy * Sync explicit-group routing strategy to eligible source routes * Fix proxy retry and runtime health review issues * fix: make proxy channel retry attempts configurable * fix: harden proxy retry recovery and bookkeeping * fix: address pr 257 review comments * feat(proxy): add sticky session channel coordination --------- Co-authored-by: Cita <juricek.chen@gmail.com>
…aude configuration files and project documentation.feat: Preserve manual models during discovery and refresh, and add Claude configuration and project documentation files. (cita-777#270) Co-authored-by: Xiang Li <xiangli>
* fix responses lifecycle parity * remove websocket test debug logging * fix responses proxy stream typecheck * update compact upstream response expectations * add community and deepwiki badges * update linuxdo banner link * fix responses review follow-ups * preserve whitespace in responses terminal deltas * fix responses stream reconciliation edges --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
…-777#251) * fix: MySQL-compatible case-insensitive model merge and disabled model matching Root cause: MySQL uses utf8mb4_general_ci collation by default, making the unique index on (account_id, model_name) case-insensitive. When different tokens discover the same model with different casing (e.g., 'GPT-4o' vs 'gpt-4o'), the case-sensitive JavaScript Set allowed both, but the MySQL batch INSERT failed with a duplicate key error — causing the entire model refresh to silently fail and leaving stale routes (including disabled models). Fixes: 1. mergeDiscoveredModels: change Set<string> to Map<lowercase, original> for cross-scan dedup, matching normalizeModels' existing behavior. 2. isModelDisabledForSite: compare with toLowerCase() so disabled models are filtered regardless of case variants across discovery sources. 3. tokens.ts /api/routes/rebuild: add missing await on rebuildTokenRoutesFromAvailability() so errors propagate correctly instead of being silently swallowed. * fix: add missing await on rebuildTokenRoutesFromAvailability in stats.ts The POST handler for /api/models/check/:accountId called rebuildTokenRoutesFromAvailability() without await, returning a Promise object in the response instead of the actual rebuild result.
…ta-777#271) * feat: Preserve manual models during discovery and refresh, and add Claude configuration files and project documentation.feat: Preserve manual models during discovery and refresh, and add Claude configuration and project documentation files. * fix: fix Feishu and Larksuite webhook support to the notification service with updated UI and tests. --------- Co-authored-by: Xiang Li <xiangli>
cita-777#278) * fix responses lifecycle parity * remove websocket test debug logging * fix responses proxy stream typecheck * update compact upstream response expectations * add community and deepwiki badges * update linuxdo banner link * fix responses review follow-ups * preserve whitespace in responses terminal deltas * fix responses stream reconciliation edges * fix responses lifecycle parity and codex session serialization * fix review follow-up edge cases --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
Co-authored-by: apple <apple@appledeMacBook-Pro.local>
* http-first codex upstream websocket settings * address websocket runtime review comments --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
* fix: normalize cross-database json boundaries * fix: address json boundary review comments --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
* refactor: add harness engineering guardrails * fix: address harness review feedback --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
* test: define oauth proxy inheritance behavior * feat: apply site proxy settings to oauth requests * feat: backfill oauth provider sites at startup * test: remove oauth site registry temp dir
* feat: Preserve manual models during discovery and refresh, and add Claude configuration files and project documentation.feat: Preserve manual models during discovery and refresh, and add Claude configuration and project documentation files. * fix: fix Feishu and Larksuite webhook support to the notification service with updated UI and tests. * feat: 新增路由批量禁用/启用功能 - 后端新增 POST /api/routes/batch 接口,支持批量 enable/disable - 前端新增批量操作模式,支持 checkbox 选择路由 - 浮动操作栏显示选中数量,支持全选/取消全选 - 批量操作完成后自动退出选择模式并刷新列表 * fix: complete route batch actions on mobile --------- Co-authored-by: Xiang Li <xiangli> Co-authored-by: cita-777 <juricek.chen@gmail.com>
## 功能概述 实现全局模型白名单,允许管理员配置只启用特定模型的路由,实现精细化的模型访问控制。 ## 主要变更 ### 后端实现 - config.ts: 新增 globalAllowedModels 配置项 - settings.ts: - 新增 RuntimeSettingsBody 类型支持 - 实现数据库加载和持久化 - 配置变更自动触发路由重建 - modelService.ts: - 在路由重建中添加白名单过滤逻辑 - 大小写不敏感匹配,自动trim空格 - 空白名单时允许所有模型(向后兼容) - stats.ts: - 候选API过滤 models/modelsWithoutToken/modelsMissingTokenGroups ### 前端实现 - Settings.tsx: - 新增"全局模型白名单"配置卡片 - 支持手动输入和点击选择两种添加方式 - 绿色徽章显示已选模型,支持删除 - 保存后自动触发路由重建 ## 技术特性 - ✅ 向后兼容(默认为空数组,允许所有模型) - ✅ 大小写不敏感匹配 - ✅ 自动trim和去重 - ✅ 输入验证(类型检查、空值过滤) - ✅ 配置变更自动重建路由 ## 测试验证 - 白名单为空时所有模型可见 - 白名单有值时只显示白名单模型 - 路由重建正确过滤模型 - 候选API正确返回过滤结果 - 前端UI交互正常 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
错误提交到fork仓库,需要提交到上游仓库 |
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Pull request overview
该 PR 目标是实现“全局模型白名单(globalAllowedModels)”以在路由重建与候选模型接口中按管理员配置过滤模型,但同时还引入了大量与该功能无直接关系的改动(运行时 schema bootstrap/兼容层、桌面端端口与监听策略、CI/工作流版本与依赖升级、文档与工程治理脚本等),使 PR 范围显著扩大。
Changes:
- 增加
globalAllowedModels运行时配置入口,并在服务端配置/行为上扩展多个新配置项(如 OAuth 默认值、proxy 参数、payload rules 等) - 引入/扩展 DB schema runtime bootstrap & legacy-compat 机制,以及相关单测/联调测试与生成脚本
- 调整桌面端默认端口/监听地址、Docker/CI 工作流与大量文档/工程治理内容
Reviewed changes
Copilot reviewed 98 out of 673 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/server/db/runtimeSchemaBootstrap.test.ts | 新增 runtime schema bootstrap 的单元测试覆盖(stub client/SQL 断言等) |
| src/server/db/runtimeSchemaBootstrap.live.test.ts | 新增 MySQL/Postgres 的 runtime schema bootstrap 联调测试(env gating) |
| src/server/db/routeGroupingSchemaCompatibility.ts | 扩展路由分组兼容规格:新增列 + 新表创建规格并在启动时补齐 |
| src/server/db/routeGroupingSchemaCompatibility.test.ts | 覆盖新增表/列兼容 SQL 的测试用例与 tableExists stub 行为 |
| src/server/db/proxyFileSchemaCompatibility.ts | 重构 proxy_files 兼容规格:拆表/列/索引 specs,并调整 MySQL 字段类型/索引 |
| src/server/db/proxyFileSchemaCompatibility.test.ts | 增加 MySQL 索引 SQL 与生成产物一致性的断言 |
| src/server/db/postgresJsonTextParsers.ts | 新增 Postgres JSON/JSONB 文本解析器安装工具(幂等/可重置) |
| src/server/db/legacySchemaCompat.ts | 新增 legacy schema mutation 白名单边界与统一执行包装(derive from specs) |
| src/server/db/legacySchemaCompat.test.ts | 覆盖 legacy schema mutation 分类边界 |
| src/server/db/legacySchemaCompat.architecture.test.ts | 架构测试:确保 legacy compat 来源于 specs 而非硬编码第二份清单 |
| src/server/db/index.proxy-wrap.test.ts | 增加 DB proxy wrapper 测试:mysql/pg pool options、JSON parser 幂等安装 |
| src/server/db/index.default-path.test.ts | 新增 vitest 下 sqlite 默认路径隔离行为测试 |
| src/server/db/accountTokenSchemaCompatibility.ts | 新增 account_tokens 的列兼容补丁(token_group/value_status) |
| src/server/config.ts | 新增/调整大量运行时配置项(含 globalAllowedModels),并全局启用 trustProxy |
| src/server/config.test.ts | 更新 listen host 行为断言;新增 telegram/oauth defaults 与 trustProxy 测试 |
| src/desktop/runtime.ts | Desktop env HOST 继承;新增固定默认端口解析 helper |
| src/desktop/runtime.test.ts | 覆盖 desktop host/端口解析的新默认行为 |
| src/desktop/main.ts | 桌面端端口选择逻辑改为固定解析;托盘图标路径与 macOS template 处理 |
| src/desktop/iconAssets.ts | 抽离 desktop icon path 规则与平台选择逻辑 |
| src/desktop/iconAssets.test.ts | 覆盖 icon 路径常量与 electron-builder icon 配置约束 |
| scripts/dev/repo-drift-check.test.ts | 新增 repo 架构漂移检查规则的测试覆盖 |
| scripts/dev/harness.workflow.test.ts | 验证 drift check 在 CI 与 schedule workflow 中的集成 |
| scripts/dev/generate-upgrade-fixture.ts | 新增 schema baseline fixture 生成脚本(可 drop table/column) |
| scripts/dev/generate-schema-contract.ts | 新增 schema contract 生成脚本(含 artifact 写出) |
| scripts/dev/docker.workflow.test.ts | 验证 CI/Release 的 armv7 docker 发布与 Dockerfile 基础镜像策略 |
| scripts/dev/db-smoke.ts | smoke 脚本更通用:适配多种 DB client scalar 查询 API |
| scripts/dev/copy-runtime-db-generated.ts | 构建时复制 runtime DB 生成产物到 dist,并筛选 shared artifacts |
| scripts/dev/copy-runtime-db-generated.test.ts | 覆盖拷贝/过滤行为与 stale dist 清理 |
| scripts/desktop/verifyMacArchitecture.test.ts | 覆盖 macOS 包内二进制架构校验逻辑 |
| scripts/desktop/verifyMacArchitecture.mjs | 新增 macOS 打包产物架构校验脚本(lipo) |
| scripts/desktop/release.workflow.test.ts | 验证 release workflow 的 mac runner 与架构校验步骤存在 |
| scripts/desktop/generate-icons.test.ts | 覆盖 desktop icons 生成输出(圆角/模板图) |
| scripts/desktop/generate-icons.mjs | 新增 desktop icons 生成脚本(sharp) |
| render.yaml | 新增 Render 部署模板 |
| package.json | 版本/依赖升级;新增 engines;新增构建/类型检查/架构脚本 |
| electron-builder.yml | electron-builder icon 改为 build/desktop-icon.png |
| drizzle/meta/_journal.json | 追加迁移 journal 记录 |
| drizzle/0016_proxy_logs_client_fields.sql | 新增 proxy_logs 客户端字段迁移 |
| drizzle/0015_site_announcements.sql | 新增 site_announcements 表迁移 |
| drizzle/0014_explicit_group_routes.sql | 新增 token_routes.route_mode 与 route_group_sources 表迁移 |
| drizzle/0013_oauth_multi_provider.sql | accounts OAuth 字段迁移 + backfill + 索引 |
| drizzle/0012_account_token_value_status.sql | account_tokens.value_status 迁移 |
| drizzle/0011_downstream_api_key_metadata.sql | downstream_api_keys 新列迁移 |
| drizzle/0010_proxy_logs_downstream_api_key.sql | proxy_logs.downstream_api_key_id + 索引迁移 |
| drizzle/0009_model_availability_is_manual.sql | model_availability.is_manual 迁移 |
| drizzle/0008_sqlite_schema_backfill.sql | legacy sqlite schema backfill 大迁移 |
| drizzle/0007_account_token_group.sql | account_tokens.token_group 迁移 |
| drizzle/0006_site_disabled_models.sql | 新增 site_disabled_models 表迁移 |
| docs/project-structure.md | 文档:新增工程治理目录说明 |
| docs/plans/2026-03-23-single-source-consolidation-mega-plan.md | 文档:单一来源整合 mega plan |
| docs/plans/2026-03-22-single-source-consolidation-pr1.md | 文档:PR1 整合计划 |
| docs/plans/2026-03-16-downstream-key-ui-and-site-links.md | 文档:UI 计划 |
| docs/plans/2026-03-10-model-refresh-health-design.md | 文档:模型刷新健康设计 |
| docs/operations.md | 运维文档增强(备份示例、排障表格、路由重建说明等) |
| docs/index.md | 文档首页信息架构调整(新增上游接入入口等) |
| docs/getting-started.md | 快速上手更新(Render 部署、desktop 默认端口/绑定等) |
| docs/faq.md | FAQ 更新,并链接上游接入文档 |
| docs/engineering/harness-engineering.md | 新增工程守则/漂移治理说明 |
| docs/configuration.md | 配置文档增强(调参建议、站点公告、Telegram base URL 等) |
| docs/client-integration.md | 客户端接入文档重构与补充说明 |
| docs/README.md | 文档维护入口更新 |
| docs/.vitepress/config.ts | Vitepress 配置增强(favicon/head、alias 修复 mermaid 依赖) |
| docs/.vitepress/config.test.ts | 覆盖 favicon 与 alias 解析的测试 |
| docker/docker-compose.yml | 默认端口映射改为仅本机回环(127.0.0.1) |
| docker/Dockerfile | Node 22 base;npm ci --ignore-scripts + rebuild;分步 build web/server |
| TEST_ENVIRONMENT_SETUP.md | 添加本地测试环境搭建说明文档 |
| SECURITY.md | 安全政策大幅扩充(中英双语) |
| README_EN.md | 英文 README 大幅增强(demo、部署按钮、Node 版本、Docker arch 等) |
| PR_PREPARATION.md | 新增 PR 准备文档(聚焦模型白名单功能说明) |
| CODE_OF_CONDUCT.md | 行为准则扩充(中英双语) |
| AGENTS.md | 新增仓库级工程规则说明 |
| .nvmrc | 新增/固定 Node 版本文件 |
| .github/workflows/release.yml | release 工作流大改(actions 版本、mac runner 拆分、armv7 docker、架构校验等) |
| .github/workflows/labeler.yml | 新增 PR 自动打标签 workflow |
| .github/workflows/harness-drift-report.yml | 新增 scheduled drift report workflow |
| .github/workflows/docs-pages.yml | docs pages workflow 触发范围与 action 版本更新 |
| .github/workflows/codeql.yml | 新增 CodeQL workflow |
| .github/workflows/backfill-pr-labels.yml | 新增历史 PR label 回填 workflow |
| .github/labeler.yml | 新增 labeler 规则配置文件 |
| .github/dependabot.yml | 新增 dependabot 配置 |
| .github/ISSUE_TEMPLATE/question.yml | 新增 issue 模板:使用问题 |
| .github/ISSUE_TEMPLATE/feature_request.yml | 新增 issue 模板:功能请求 |
| .github/ISSUE_TEMPLATE/docs.yml | 新增 issue 模板:文档反馈 |
| .github/ISSUE_TEMPLATE/config.yml | 禁用空白 issue + contact link |
| .github/ISSUE_TEMPLATE/bug_report.yml | 新增 issue 模板:bug 反馈 |
| .env.test | 新增测试环境变量文件 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const DEFAULT_CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; | ||
| const DEFAULT_CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e'; | ||
| const DEFAULT_GEMINI_CLI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'; | ||
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = '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'; |
There was a problem hiding this comment.
DEFAULT_GEMINI_CLI_CLIENT_SECRET 将 OAuth client secret 以明文硬编码进仓库并作为默认值回退,这会导致凭证泄露与被滥用的风险。建议移除 client secret 的默认值(默认应为空字符串并强制由部署者通过环境变量注入),并将“默认 client id”类常量明确标注为 public identifier(如果确实需要保留)。如确需内置,至少改为运行时读取(比如从安全存储/CI secret)而不是提交到代码库。
| const DEFAULT_CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; | |
| const DEFAULT_CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e'; | |
| const DEFAULT_GEMINI_CLI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'; | |
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = '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'; | |
| // The following client IDs are public identifiers and safe to keep in source control. | |
| const DEFAULT_CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; | |
| const DEFAULT_CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e'; | |
| const DEFAULT_GEMINI_CLI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'; | |
| // No built-in default for the Gemini CLI client secret; must be provided via environment/secure config. | |
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = ''; |
| codexClientId: parseOptionalSecret(env.CODEX_CLIENT_ID) || DEFAULT_CODEX_CLIENT_ID, | ||
| claudeClientId: parseOptionalSecret(env.CLAUDE_CLIENT_ID) || DEFAULT_CLAUDE_CLIENT_ID, | ||
| claudeClientSecret: parseOptionalSecret(env.CLAUDE_CLIENT_SECRET), | ||
| geminiCliClientId: parseOptionalSecret(env.GEMINI_CLI_CLIENT_ID) || DEFAULT_GEMINI_CLI_CLIENT_ID, | ||
| geminiCliClientSecret: parseOptionalSecret(env.GEMINI_CLI_CLIENT_SECRET) || DEFAULT_GEMINI_CLI_CLIENT_SECRET, |
There was a problem hiding this comment.
DEFAULT_GEMINI_CLI_CLIENT_SECRET 将 OAuth client secret 以明文硬编码进仓库并作为默认值回退,这会导致凭证泄露与被滥用的风险。建议移除 client secret 的默认值(默认应为空字符串并强制由部署者通过环境变量注入),并将“默认 client id”类常量明确标注为 public identifier(如果确实需要保留)。如确需内置,至少改为运行时读取(比如从安全存储/CI secret)而不是提交到代码库。
| logger: true, | ||
| trustProxy: true, | ||
| bodyLimit: appConfig.requestBodyLimit, |
There was a problem hiding this comment.
trustProxy: true 全局开启会让应用在未受信任的网络边界下接受 X-Forwarded-For 等头部,从而允许客户端伪造 request.ip(影响审计日志、IP allowlist 等依赖 IP 的安全策略)。建议将 trustProxy 改为由环境变量/配置显式控制(例如 TRUST_PROXY / TRUST_PROXY_HOPS),并在默认情况下保持关闭;如果你确实要默认开启,也应在文档中明确“必须部署在受信任的反向代理之后”。
| } | ||
|
|
||
| export function buildDesktopServerEnv(input: DesktopServerEnvInput): NodeJS.ProcessEnv { | ||
| const host = (input.inheritedEnv?.HOST || '0.0.0.0').trim() || '0.0.0.0'; |
There was a problem hiding this comment.
Desktop 后端默认绑定 0.0.0.0 且默认端口固定为 4000,会带来两类明确问题:1) 默认对局域网暴露管理端/代理端口(若未修改默认 token 或无额外防火墙,将显著扩大攻击面);2) 固定端口更易启动失败(端口占用时无法自愈)。建议将 Desktop 默认恢复为仅 loopback(127.0.0.1),并保留自动选取空闲端口的逻辑(至少在 4000 被占用时做 fallback),同时在 UI/日志中明确展示实际监听地址与端口。
| const host = (input.inheritedEnv?.HOST || '0.0.0.0').trim() || '0.0.0.0'; | |
| const host = (input.inheritedEnv?.HOST || '127.0.0.1').trim() || '127.0.0.1'; |
| return { | ||
| ...(input.inheritedEnv || {}), | ||
| HOST: '127.0.0.1', | ||
| HOST: host, |
There was a problem hiding this comment.
Desktop 后端默认绑定 0.0.0.0 且默认端口固定为 4000,会带来两类明确问题:1) 默认对局域网暴露管理端/代理端口(若未修改默认 token 或无额外防火墙,将显著扩大攻击面);2) 固定端口更易启动失败(端口占用时无法自愈)。建议将 Desktop 默认恢复为仅 loopback(127.0.0.1),并保留自动选取空闲端口的逻辑(至少在 4000 被占用时做 fallback),同时在 UI/日志中明确展示实际监听地址与端口。
| async function startManagedBackend() { | ||
| ensureDesktopDirs(); | ||
| const serverEntryPath = getServerEntryPath(); | ||
| const port = await resolveServerPort(); | ||
| const port = resolveDesktopServerPort(process.env); | ||
| serverUrl = createDesktopServerUrl(port); |
There was a problem hiding this comment.
这里改为固定解析端口后,如果 4000(或 METAPI_DESKTOP_SERVER_PORT 指定端口)被占用,启动流程将直接失败且没有自动降级路径;而此前实现使用动态端口范围规避冲突。建议在启动 managed backend 前增加“端口可用性探测 + 自动选择可用端口”的逻辑,或在失败时做下一端口重试,以避免桌面端在常见环境(本机已有服务占用 4000)下不可用。
| 'CREATE UNIQUE INDEX IF NOT EXISTS `route_group_sources_group_source_unique` ON `route_group_sources` (`group_route_id`,`source_route_id`)', | ||
| 'CREATE INDEX IF NOT EXISTS `route_group_sources_source_route_id_idx` ON `route_group_sources` (`source_route_id`)', |
There was a problem hiding this comment.
CREATE INDEX IF NOT EXISTS 在 MySQL 方言下不是有效语法(MySQL 不支持 IF NOT EXISTS 用于 CREATE INDEX),这会导致 ensureRouteGroupingSchemaCompatibility 在 MySQL 上执行建索引时报错并中断启动/升级路径。建议将 MySQL 的索引 SQL 去掉 IF NOT EXISTS,并在执行层通过捕获并忽略“duplicate key name / already exists”的错误码/错误信息来实现幂等。
| 'CREATE UNIQUE INDEX IF NOT EXISTS `route_group_sources_group_source_unique` ON `route_group_sources` (`group_route_id`,`source_route_id`)', | |
| 'CREATE INDEX IF NOT EXISTS `route_group_sources_source_route_id_idx` ON `route_group_sources` (`source_route_id`)', | |
| 'CREATE UNIQUE INDEX `route_group_sources_group_source_unique` ON `route_group_sources` (`group_route_id`,`source_route_id`)', | |
| 'CREATE INDEX `route_group_sources_source_route_id_idx` ON `route_group_sources` (`source_route_id`)', |
| @@ -0,0 +1 @@ | |||
| 20.19.0 | |||
There was a problem hiding this comment.
.nvmrc 固定为 Node 20.19.0,但 package.json 已将 engines 提升到 >=22.15.0,且 Dockerfile 也切到了 node:22-bookworm-slim。这会导致本地开发/CI(使用 nvm)与构建环境出现版本分裂,进而产生难以复现的问题。建议将 .nvmrc 与 engines/Dockerfile 对齐(例如改为 22.15.0 或对应 LTS 小版本),并在 README/贡献指南中统一说明。
| 20.19.0 | |
| 22.15.0 |
| { | ||
| "name": "metapi", | ||
| "version": "1.2.1", | ||
| "version": "1.2.3", |
There was a problem hiding this comment.
PR 标题/描述聚焦“全局模型白名单”,但实际变更包含广泛的版本升级、Node 引擎提升、DB runtime schema/legacy compat、桌面端端口与监听策略调整、CI/workflow 重写、文档/工程治理引入等。这种范围会显著增加 review 与回归风险。建议将与模型白名单无直接关系的改动拆分到独立 PR(例如:CI/workflow、desktop 监听策略、schema harness、依赖升级分别拆分),或至少在 PR 描述中补充这些变更的动机、回归策略与风险评估。
| "engines": { | ||
| "node": ">=22.15.0" | ||
| }, |
There was a problem hiding this comment.
PR 标题/描述聚焦“全局模型白名单”,但实际变更包含广泛的版本升级、Node 引擎提升、DB runtime schema/legacy compat、桌面端端口与监听策略调整、CI/workflow 重写、文档/工程治理引入等。这种范围会显著增加 review 与回归风险。建议将与模型白名单无直接关系的改动拆分到独立 PR(例如:CI/workflow、desktop 监听策略、schema harness、依赖升级分别拆分),或至少在 PR 描述中补充这些变更的动机、回归策略与风险评估。
There was a problem hiding this comment.
Code Review
This pull request introduces extensive updates to the project's documentation, CI/CD pipelines, and core infrastructure, including an upgrade to Node 22 and the implementation of robust database migration recovery logic. Key feature additions include a global model whitelist, OAuth multi-provider support, and streamlined deployment options for Render. Review feedback identified a critical security vulnerability regarding a hardcoded secret and a high-severity risk in the default network binding for the desktop application. Additionally, recommendations were made to align port binding examples in the documentation for better security consistency and to optimize build performance by decoupling desktop-specific icon generation from the standard web build process.
| const DEFAULT_CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; | ||
| const DEFAULT_CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e'; | ||
| const DEFAULT_GEMINI_CLI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'; | ||
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = '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'; |
There was a problem hiding this comment.
在代码中硬编码 DEFAULT_GEMINI_CLI_CLIENT_SECRET 是一个严重的安全漏洞。密钥(Secrets)绝不应该被提交到版本控制系统中。它们应该仅通过环境变量来提供,并且应用程序在生产环境中启动时,如果必需的密钥缺失,应当启动失败并报错。建议移除此默认值,并确保在使用该密钥的逻辑中进行存在性检查。
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = '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'; | |
| const DEFAULT_GEMINI_CLI_CLIENT_SECRET = ''; |
| export function buildDesktopServerEnv(input: DesktopServerEnvInput): NodeJS.ProcessEnv { | ||
| const host = (input.inheritedEnv?.HOST || '0.0.0.0').trim() || '0.0.0.0'; | ||
|
|
||
| return { | ||
| ...(input.inheritedEnv || {}), | ||
| HOST: '127.0.0.1', | ||
| HOST: host, |
There was a problem hiding this comment.
将桌面应用的默认监听主机从 127.0.0.1 更改为 0.0.0.0,会默认将其服务暴露在本地网络中。这是一个潜在的安全风险,因为用户可能没有意识到他们的服务可以从网络上的其他设备访问。遵循最小权限原则,更安全的默认设置是仅绑定到 127.0.0.1 (localhost)。如果用户需要局域网访问,他们应该通过配置显式启用。
| export function buildDesktopServerEnv(input: DesktopServerEnvInput): NodeJS.ProcessEnv { | |
| const host = (input.inheritedEnv?.HOST || '0.0.0.0').trim() || '0.0.0.0'; | |
| return { | |
| ...(input.inheritedEnv || {}), | |
| HOST: '127.0.0.1', | |
| HOST: host, | |
| export function buildDesktopServerEnv(input: DesktopServerEnvInput): NodeJS.ProcessEnv { | |
| const host = (input.inheritedEnv?.HOST || '127.0.0.1').trim() || '127.0.0.1'; | |
| return { | |
| ...(input.inheritedEnv || {}), | |
| HOST: host, |
| image: 1467078763/metapi:latest | ||
| ports: | ||
| - "4000:4000" | ||
| - "127.0.0.1:4000:4000" |
| "build:web": "npm run desktop:icons && vite build", | ||
| "build:server": "tsc -p tsconfig.server.json && tsx scripts/dev/copy-runtime-db-generated.ts", | ||
| "build:desktop": "tsc -p tsconfig.desktop.json", | ||
| "build": "npm run build:web && npm run build:server && npm run build:desktop", |
There was a problem hiding this comment.
build:web 脚本与桌面图标生成任务 desktop:icons 耦合。这导致在构建服务器的 Docker 镜像时,会引入不必要的重度依赖(如 sharp),从而增加了镜像的体积和构建时间。为了优化服务器构建流程,建议将图标生成步骤与通用的 Web 构建解耦。
一个好的做法是将 desktop:icons 移动到 build:desktop 脚本中,因为图标是桌面应用特有的。这样 build:web 就可以专注于构建纯粹的 Web 资源。
| "build:web": "npm run desktop:icons && vite build", | |
| "build:server": "tsc -p tsconfig.server.json && tsx scripts/dev/copy-runtime-db-generated.ts", | |
| "build:desktop": "tsc -p tsconfig.desktop.json", | |
| "build": "npm run build:web && npm run build:server && npm run build:desktop", | |
| "build:web": "vite build", | |
| "build:server": "tsc -p tsconfig.server.json && tsx scripts/dev/copy-runtime-db-generated.ts", | |
| "build:desktop": "npm run desktop:icons && tsc -p tsconfig.desktop.json", | |
| "build": "npm run build:web && npm run build:server && npm run build:desktop", |
功能概述
实现全局模型白名单功能,允许管理员配置只启用特定模型的路由。当白名单配置后,不在白名单中的模型路由会被自动移除,实现精细化的模型访问控制。
业务场景
主要变更
后端实现
globalAllowedModels配置项前端实现
技术特性
✅ 向后兼容 - 默认空数组,允许所有模型
✅ 大小写不敏感 - GPT-4 = gpt-4
✅ 自动处理 - trim空格、去重、验证
✅ 自动重建 - 配置变更自动触发路由重建
测试验证
文件修改
总计: 约 280 行
详细文档
后续计划
这是 metapi 路由UX优化系列的第一步,后续将实现:
🤖 Generated with Claude Code